Onvif协议:IPC客户端开发之鉴权 您所在的位置:网站首页 onvif ipnc Onvif协议:IPC客户端开发之鉴权

Onvif协议:IPC客户端开发之鉴权

2023-10-16 07:24| 来源: 网络整理| 查看: 265

引言

Onvif规定,有些接口需要鉴权,有些节点不需要鉴权。那么,怎么知道哪些接口需要认证呢?

ONVIF哪些接口需要认证

在官网的ONVIF Core Specification文档中有详细的规定,如Version 16.12的版本为《ONVIF-Core-Specification-v1612.pdf》。在该文档的「Access classes for service requests」章节中有接口访问权限的相关规定,如下图所示。比如「PRE_AUTH」的规定是:The service shall not require user authentication,那非「PRE_AUTH」的就是都要认证的。 在这里插入图片描述 拿GetServices接口举个例子,在ONVIF Core Specification文档中找到GetServices接口定义(如下图所示),会有Access Class: PRE_AUTH的说明,表明客户端调用该接口时,不需要携带用户名、密码认证信息。 在这里插入图片描述 再看看GetDeviceInformation接口规定(如下图所示),Access Class: READ_SYSTEM,说明客户端调用该接口是需要进行认证。 在这里插入图片描述

实践 生成框架源码

1、编译gsoap,准备好环境

2、生成头文件 (1) 建立一个目录onvif-auth,目录下有两个目录bin和gsoap 在这里插入图片描述

(2) 编写脚本onvif_head.sh,生成头文件

onvif-auth]$ ls bin gsoap onvif_head.sh onvif-auth]$ cat onvif_head.sh #!/bin/bash mkdir onvif_head cd onvif_head ../bin/wsdl2h -o onvif.h -s -d -x -t ../gsoap/WS/typemap.dat \ http://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl \ http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl onvif-auth]$ chmod 777 onvif_head.sh onvif-auth]$ ./onvif_head.sh Saving onvif.h ** The gSOAP WSDL/WADL/XSD processor for C and C++, wsdl2h release 2.8.117 ** Copyright (C) 2000-2021 Robert van Engelen, Genivia Inc. ** All Rights Reserved. This product is provided "as is", without any warranty. ** The wsdl2h tool and its generated software are released under the GPL. ** ---------------------------------------------------------------------------- ** A commercial use license is available from Genivia Inc., [email protected] ** ---------------------------------------------------------------------------- Reading type definitions from type map "../gsoap/WS/typemap.dat" Connecting to 'http://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl' to retrieve WSDL/WADL or XSD... connected, receiving... Done reading 'http://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl' Connecting to 'http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl' to retrieve WSDL/WADL or XSD... connected, receiving... Connecting to 'http://www.onvif.org/onvif/ver10/schema/onvif.xsd' to retrieve schema... connected, receiving... Connecting to 'http://docs.oasis-open.org/wsn/b-2.xsd' to retrieve schema... connected, receiving... Connecting to 'http://docs.oasis-open.org/wsrf/bf-2.xsd' to retrieve schema... connected, receiving... Done reading 'http://docs.oasis-open.org/wsrf/bf-2.xsd' Connecting to 'http://docs.oasis-open.org/wsn/t-1.xsd' to retrieve schema... connected, receiving... Done reading 'http://docs.oasis-open.org/wsn/t-1.xsd' Done reading 'http://docs.oasis-open.org/wsn/b-2.xsd' Connecting to 'http://www.onvif.org/onvif/ver10/schema/common.xsd' to retrieve schema... connected, receiving... Done reading 'http://www.onvif.org/onvif/ver10/schema/common.xsd' Done reading 'http://www.onvif.org/onvif/ver10/schema/onvif.xsd' Done reading 'http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl' Note: option -p auto-enabled to generate wrappers for built-in types derived from xsd__anyType to support polymorphism in XML by (de)serializing any derived type of xsd:anyType (or xsd:anySimpleType) as elements annotated by xsi:type attributes in XML, use option -P to suppress and disable this feature Warning: 2 service bindings found, but collected as one service (use option -Nname to produce a separate service for each binding) To finalize code generation, execute: > soapcpp2 onvif.h Or to generate C++ proxy and service classes: > soapcpp2 -j onvif.h

(3)修改上面生成的onvif.h: 在onvif.h头文件开头加入:

#import "wsse.h"

注意是import而不是include 在这里插入图片描述 打开wsse.h,可以看到相应依赖 在这里插入图片描述 注意:依赖一定要自己去看,否则会出现一大堆未定义引用。我在这里困扰了两天

错误: Cannot connect to https site: SSL/TLS support not enabled in this version。 因为wsdl2h 编译时没有关闭了openssl,所以不能https下载: 将https://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl,改成http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl就可以了

(2) 编写脚本gen_code.sh, 根据上面生成的头文件生成源文件

onvif-auth]$ ls bin gen_code.sh gsoap onvif_head onvif_head.sh onvif-auth]$ cat gen_code.sh #!/bin/bash DIR=soap mkdir $DIR cd $DIR ../bin/soapcpp2 -2 -x -C ../onvif_head/onvif.h -L -I ../gsoap/import -I ../gsoap/ #----------执行------------------- onvif-auth]$ chmod 777 gen_code.sh onvif-auth]$ ./gen_code.sh ** The gSOAP code generator for C and C++, soapcpp2 release 2.8.117 ** Copyright (C) 2000-2021, Robert van Engelen, Genivia Inc. ** All Rights Reserved. This product is provided "as is", without any warranty. ** The soapcpp2 tool and its generated software are released under the GPL. ** ---------------------------------------------------------------------------- ** A commercial use license is available from Genivia Inc., [email protected] ** ---------------------------------------------------------------------------- wsa5.h(280): *WARNING*: Duplicate declaration of 'SOAP_ENV__Fault' (already declared at line 268) wsa5.h(290): **ERROR**: service operation name clash: struct/class 'SOAP_ENV__Fault' already declared at wsa.h:278 soap12.h(54): *WARNING*: option -1 or -2 overrides SOAP-ENV namespace soap12.h(55): *WARNING*: option -1 or -2 overrides SOAP-ENC namespace Saving soapStub.h annotated copy of the source interface header file Saving soapH.h serialization functions to #include in projects Using wsdd service name: wsdd Using wsdd service style: document Using wsdd service encoding: literal Using wsdd schema import namespace: http://schemas.xmlsoap.org/ws/2005/04/discovery Saving wsdd.nsmap namespace mapping table Using tdn service name: RemoteDiscoveryBinding Using tdn service style: document Using tdn service encoding: literal Using tdn schema namespace: http://www.onvif.org/ver10/network/wsdl Saving RemoteDiscoveryBinding.nsmap namespace mapping table Using tds service name: DeviceBinding Using tds service style: document Using tds service encoding: literal Using tds schema namespace: http://www.onvif.org/ver10/device/wsdl Saving DeviceBinding.nsmap namespace mapping table Saving soapClient.cpp client call stub functions Saving soapC.cpp serialization functions There were errors: 1 semantic error 3 warnings

可以看到,上面出现了错误:

wsa5.h(290): **ERROR**: service operation name clash: struct/class 'SOAP_ENV__Fault' already declared at wsa.h:278

解决:打开 gsoap\import 路径下的wsa5.h, 将277行的SOAP_ENV__Fault结构体注释掉(改成其他名字亦可) 在这里插入图片描述 重新执行:

#----------执行------------------- onvif-auth]$ chmod 777 gen_code.sh onvif-auth]$ ./gen_code.sh mkdir: 无法创建目录"soap": 文件已存在 ** The gSOAP code generator for C and C++, soapcpp2 release 2.8.117 ** Copyright (C) 2000-2021, Robert van Engelen, Genivia Inc. ** All Rights Reserved. This product is provided "as is", without any warranty. ** The soapcpp2 tool and its generated software are released under the GPL. ** ---------------------------------------------------------------------------- ** A commercial use license is available from Genivia Inc., [email protected] ** ---------------------------------------------------------------------------- soap12.h(54): *WARNING*: option -1 or -2 overrides SOAP-ENV namespace soap12.h(55): *WARNING*: option -1 or -2 overrides SOAP-ENC namespace Saving soapStub.h annotated copy of the source interface header file Saving soapH.h serialization functions to #include in projects Using wsdd service name: wsdd Using wsdd service style: document Using wsdd service encoding: literal Using wsdd schema import namespace: http://schemas.xmlsoap.org/ws/2005/04/discovery Saving wsdd.nsmap namespace mapping table Using tdn service name: RemoteDiscoveryBinding Using tdn service style: document Using tdn service encoding: literal Using tdn schema namespace: http://www.onvif.org/ver10/network/wsdl Saving RemoteDiscoveryBinding.nsmap namespace mapping table Using tds service name: DeviceBinding Using tds service style: document Using tds service encoding: literal Using tds schema namespace: http://www.onvif.org/ver10/device/wsdl Saving DeviceBinding.nsmap namespace mapping table Saving soapClient.cpp client call stub functions Saving soapC.cpp serialization functions Compilation successful (2 warnings)

可以看到编译成功

编写代码

1、为了方便,我们将所有需要的代码放到同一个目录中。我们在刚才建立的onvif-auth目录下创建一个application目录,存放我们需要的所有代码(比如soap下生成的soapC.cpp、soapClient.cpp、soapH.h、soapStub.h、wsdd.nsmap;gsoap源码目录下的stdsoap2.cpp、stdsoap2.h,gsoap/plugin目录下的wsseapi.h、wsseapi.cpp、smdevp.h、smdevp.cpp、mecevp.cpp、mecevp.h、threads.cpp、threads.h、wsaapi.cpp、wsaapi.h等)。并创建一个main.cpp,如下(注意,如果只有.c没有.cpp的,那么就将其拷贝到application目录下,然后将尾部改成.cpp): 在这里插入图片描述

2、在application目录创建一个CMakeLists.txt文件,CMakeLists.txt文件内容如下:

cmake_minimum_required(VERSION 3.16) project(onvif-auth) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_NONAMESPACES -DWITH_DOM -DWITH_OPENSSL") include_directories(${PROJECT_SOURCE_DIR}/include) include_directories(/usr/include) add_definitions("-Wall -g ") aux_source_directory(. SRC_LIST) add_executable(${PROJECT_NAME} ${SRC_LIST} soapStub.h) target_link_libraries(${PROJECT_NAME} -lcrypto -lssl)

main.cpp内容如下:

#include #include "soapH.h" #include "wsdd.nsmap" #include "soapStub.h" #define SOAP_ASSERT assert #define SOAP_DBGLOG printf #define SOAP_DBGERR printf #define SOAP_TO "urn:schemas-xmlsoap-org:ws:2005:04:discovery" #define SOAP_ACTION "http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe" #define SOAP_MCAST_ADDR "soap.udp://239.255.255.250:3702" // onvif规定的组播地址 #define SOAP_ITEM "" // 寻找的设备范围 #define SOAP_TYPES "dn:NetworkVideoTransmitter" // 寻找的设备类型 #define SOAP_SOCK_TIMEOUT (10) // socket超时时间(单秒秒) void soap_perror(struct soap *soap, const char *str) { if (nullptr == str) { SOAP_DBGERR("[soap] error: %d, %s, %s\n", soap->error, *soap_faultcode(soap), *soap_faultstring(soap)); } else { SOAP_DBGERR("[soap] %s error: %d, %s, %s\n", str, soap->error, *soap_faultcode(soap), *soap_faultstring(soap)); } } void* ONVIF_soap_malloc(struct soap *soap, unsigned int n) { void *p = nullptr; if (n > 0) { p = soap_malloc(soap, n); SOAP_ASSERT(nullptr != p); memset(p, 0x00 ,n); } return p; } struct soap *ONVIF_soap_new(int timeout) { struct soap *soap = nullptr; // soap环境变量 SOAP_ASSERT(nullptr != (soap = soap_new())); soap_set_namespaces(soap, namespaces); // 设置soap的namespaces soap->recv_timeout = timeout; // 设置超时(超过指定时间没有数据就退出) soap->send_timeout = timeout; soap->connect_timeout = timeout; #if defined(__linux__) || defined(__linux) // 参考https://www.genivia.com/dev.html#client-c的修改: soap->socket_flags = MSG_NOSIGNAL; // To prevent connection reset errors #endif soap_set_mode(soap, SOAP_C_UTFSTRING); // 设置为UTF-8编码,否则叠加中文OSD会乱码 return soap; } void ONVIF_soap_delete(struct soap *soap) { soap_destroy(soap); // remove deserialized class instances (C++ only) soap_end(soap); // Clean up deserialized data (except class instances) and temporary data soap_done(soap); // Reset, close communications, and remove callbacks soap_free(soap); // Reset and deallocate the context created with soap_new or soap_copy } /************************************************************************ **函数:ONVIF_init_header **功能:初始化soap描述消息头 **参数: [in] soap - soap环境变量 **返回:无 **备注: 1). 在本函数内部通过ONVIF_soap_malloc分配的内存,将在ONVIF_soap_delete中被释放 ************************************************************************/ void ONVIF_init_header(struct soap *soap) { struct SOAP_ENV__Header *header = nullptr; SOAP_ASSERT(nullptr != soap); header = (struct SOAP_ENV__Header *)ONVIF_soap_malloc(soap, sizeof(struct SOAP_ENV__Header)); soap_default_SOAP_ENV__Header(soap, header); header->wsa__MessageID = "shsuishsihsishsishsuisbjshsusisuisusi"; header->wsa__To = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_TO) + 1); header->wsa__Action = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_ACTION) + 1); strcpy(header->wsa__To, SOAP_TO); strcpy(header->wsa__Action, SOAP_ACTION); soap->header = header; } /************************************************************************ **函数:ONVIF_init_ProbeType **功能:初始化探测设备的范围和类型 **参数: [in] soap - soap环境变量 [out] probe - 填充要探测的设备范围和类型 **返回: 0表明探测到,非0表明未探测到 **备注: 1). 在本函数内部通过ONVIF_soap_malloc分配的内存,将在ONVIF_soap_delete中被释放 ************************************************************************/ void ONVIF_init_ProbeType(struct soap *soap, struct wsdd__ProbeType *probe) { struct wsdd__ScopesType *scope = nullptr; // 用于描述查找哪类的Web服务 SOAP_ASSERT(nullptr != soap); SOAP_ASSERT(nullptr != probe); scope = (struct wsdd__ScopesType *)ONVIF_soap_malloc(soap, sizeof(struct wsdd__ScopesType)); soap_default_wsdd__ScopesType(soap, scope); // 设置寻找设备的范围 scope->__item = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_ITEM) + 1); strcpy(scope->__item, SOAP_ITEM); memset(probe, 0x00, sizeof(struct wsdd__ProbeType)); soap_default_wsdd__ProbeType(soap, probe); probe->Scopes = scope; probe->Types = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_TYPES) + 1); // 设置寻找设备的类型 strcpy(probe->Types, SOAP_TYPES); } void ONVIF_DetectDevice(void (*cb)(char *DeviceXAddr)) { int i; int result = 0; unsigned int count = 0; // 搜索到的设备个数 struct soap *soap = nullptr; // soap环境变量 struct wsdd__ProbeType req; // 用于发送Probe消息 struct __wsdd__ProbeMatches rep; // 用于接收Probe应答 struct wsdd__ProbeMatchType *probeMatch; SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT))); ONVIF_init_header(soap); // 设置消息头描述 ONVIF_init_ProbeType(soap, &req); // 设置寻找的设备的范围和类型 result = soap_send___wsdd__Probe(soap, SOAP_MCAST_ADDR, nullptr, &req); // 向组播地址广播Probe消息 while (SOAP_OK == result) // 开始循环接收设备发送过来的消息 { memset(&rep, 0x00, sizeof(rep)); result = soap_recv___wsdd__ProbeMatches(soap, &rep); if (SOAP_OK == result) { if (soap->error) { soap_perror(soap, "ProbeMatches"); } else { // 成功接收到设备的应答消息 printf("__sizeProbeMatch:%d\n",rep.wsdd__ProbeMatches->__sizeProbeMatch); if (nullptr != rep.wsdd__ProbeMatches) { count += rep.wsdd__ProbeMatches->__sizeProbeMatch; for(i = 0; i __sizeProbeMatch; i++) { probeMatch = rep.wsdd__ProbeMatches->ProbeMatch + i; if (nullptr != cb) { cb(probeMatch->XAddrs); // 使用设备服务地址执行函数回调 } } } } } else if (soap->error) { break; } } SOAP_DBGLOG("\ndetect end! It has detected %d devices!\n", count); if (nullptr != soap) { ONVIF_soap_delete(soap); } } #define SOAP_CHECK_ERROR(result, soap, str) \ do { \ if (SOAP_OK != (result) || SOAP_OK != (soap)->error) { \ soap_perror((soap), (str)); \ if (SOAP_OK == (result)) { \ (result) = (soap)->error; \ } \ goto EXIT; \ } \ } while (0) #define USERNAME "admin" #define PASSWORD "hik12345" #include "wsseapi.h" /************************************************************************ **函数:ONVIF_SetAuthInfo **功能:设置认证信息 **参数: [in] soap - soap环境变量 [in] username - 用户名 [in] password - 密码 **返回: 0表明成功,非0表明失败 **备注: ************************************************************************/ static int ONVIF_SetAuthInfo(struct soap *soap, const char *username, const char *password) { int result = 0; SOAP_ASSERT(nullptr != username); SOAP_ASSERT(nullptr != password); result = soap_wsse_add_UsernameTokenDigest(soap, NULL, username, password); SOAP_CHECK_ERROR(result, soap, "add_UsernameTokenDigest"); EXIT: return result; } /************************************************************************ **函数:ONVIF_GetDeviceInformation **功能:获取设备基本信息 **参数: [in] DeviceXAddr - 设备服务地址 **返回: 0表明成功,非0表明失败 **备注: ************************************************************************/ int ONVIF_GetDeviceInformation(const char *DeviceXAddr) { int result = 0; struct soap *soap = nullptr; _tds__GetDeviceInformation devinfo_req; _tds__GetDeviceInformationResponse devinfo_resp; SOAP_ASSERT(nullptr != DeviceXAddr); SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT))); ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD); result = soap_call___tds__GetDeviceInformation(soap, DeviceXAddr, nullptr, &devinfo_req, devinfo_resp); SOAP_CHECK_ERROR(result, soap, "GetDeviceInformation"); std::cout ONVIF_GetDeviceInformation(DeviceXAddr); } int main(int argc, char **argv) { ONVIF_DetectDevice(cb_discovery); return 0; }

3、在application创建一个build目录,进入build目录之后:

cmake .. make 对应代码

因为鉴权比较麻烦,所以我上传了一个示例工程: 代码,可以零积分下载,需要自取

其他文章 Onvif协议:理解什么是Web ServicesOnvif协议:使用gSOAP创建SOAP调用实例Onvif协议:门外汉理解ONVIF协议Onvif协议:到底什么是ONVIF协议Onvif协议:实现Probe命令来进行设备发现(discover)Onvif协议:IPC客户端开发之获取设备基本信息Onvif协议:IPC客户端开发之获取设备能力Onvif协议:IPC客户端开发之鉴权Onvif协议:IPC客户端开发之PTZ控制Onvif协议:IPC客户端开发之获取实时预览的Url地址Onvif协议:IPC客户端开发之图像抓拍ONVIF Device Test Tool测试工具使用方法


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有